Os dados de focos foram obtidos do banco de dados do INPE: https://queimadas.dgi.inpe.br/queimadas/bdqueimadas#exportar-dados
Os dados foram filtrados pelos limites Amazônia legal, entre os anos de 2011 e 2020.
A distância à rodovia mais próxima foi calculada em função de um shapefile de rodovias disponibilizado pelo IBGE. O mapa de grau de urbanização também foi obtido do IBGE. Os dados de áreas desmatadas são elaborados pelo INPE através do PRODES.
Os pacotes sf e terra são essenciais para a manipulação de dados vetoriais e matriciais respectivamente. O pacote tidyverse contém funções uteis para a manipulação de tabelas. O pacote spatstat é utilizado para extrair estatisticas espaciais como a densidade de focos. O pacote caret é responsável pelo treinamento e aplicação dos modelos.
library(terra)
library(tidyverse)
library(sf)
library(spatstat)
library(caret)
library(doParallel)
A ideia geral aqui é criar uma imagem raster com a contagem da ocorrência de focos por km². Primeiro os dados de foco devem ser carregados. Eu aproveito para ler o shapefile da amazônia legal também.
focos <- st_read("focos_2011_2020.shp")
## Reading layer `focos_2011_2020' from data source
## `F:\Pablo\Documentos\Doutorado\side\focos_2011_2020.shp' using driver `ESRI Shapefile'
## Simple feature collection with 1215884 features and 15 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: 2828897 ymin: 8004499 xmax: 6111664 ymax: 10573720
## Projected CRS: SIRGAS 2000 / Brazil Polyconic
am_legal <- st_read("amazonia_legal.shp")
## Reading layer `amazonia_legal' from data source
## `F:\Pablo\Documentos\Doutorado\side\amazonia_legal.shp' using driver `ESRI Shapefile'
## Simple feature collection with 1 feature and 1 field
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 2794537 ymin: 8004219 xmax: 6112012 ymax: 10586380
## Projected CRS: SIRGAS 2000 / Brazil Polyconic
Em seguida criar um raster vazio de resolução de 10km para preencher os dados:
focos_km2 <- rast(ext=ext(focos), crs=crs(focos), resolution=10000)
E enfim contar os focos por pixel da imagem:
focos_km2 <- rasterize(vect(focos), focos_km2, fun=length, field=1, background=0) %>%
mask(vect(am_legal))
colorPal <- colorRampPalette(rev(RColorBrewer::brewer.pal(11, "RdYlGn")))(256)
plot(main="Focos brutos por 10km²", focos_km2, col=colorPal)
Ou então fazer uma análise de densidade:
pts <- st_coordinates(focos) %>%
unique()
pts_ppp <- ppp(pts[,1], pts[,2], window=as.owin(am_legal), check=FALSE)
A densidade é diferente da quantidade bruta de focos por pixel no sentido de que ela é suavizada em função de uma banda (distância). A função padrão de suavização é a gaussiana.
Um teste rapido usando diferentes bandas:
bandas <- c(10000, 25000, 50000, 100000)
dens <- lapply(bandas, function(x) rast(density(pts_ppp, sigma=x))*1e6)
dens <- rast(dens)
names(dens) <- c("10km", "25km", "50km", "100km")
plot(dens, col=colorPal)
Nesse caso uma banda de 50km pareceu apropriada.
pt_dens <- density(pts_ppp, sigma=50000, eps=10000)
pt_dens <- rast(pt_dens)*1e6
crs(pt_dens) <- crs(focos_km2)
pt_dens <- resample(pt_dens, focos_km2)
plot(main="Densidade de focos por km²", pt_dens, col=colorPal)
Lado a lado:
r_stack <- rast(list(focos_km2, pt_dens))
names(r_stack) <- c("Focos por 10km²", "Densidade de focos")
plot(r_stack, col=colorPal)
É importante lembrar que os valores calculados representam um histórico de 10 anos, portanto o valor anual seria a média, ou o valor apresentado dividido por 10.
Finalmente, salvar os resultados:
writeRaster(focos_km2, "focos_10km.tif", gdal=c("COMPRESS=DEFLATE", "PREDICTOR=2"), overwrite=T)
writeRaster(pt_dens, "densidade.tif", gdal=c("COMPRESS=DEFLATE", "PREDICTOR=2"), overwrite=T)
Os mapas dessas variáveis ja foi feito usando o QGIS, então é só carregar eles.
dist_rod <- rast("dist_rod.tif") %>%
mask(vect(am_legal))
plot(main="Distância à rodovia mais próxima (km)", dist_rod/1000, col=viridis::viridis(256))
dist_desm <- rast("dist_desm.tif") %>%
mask(vect(am_legal))
plot(main="Distância à área desmatada mais próxima (km)", dist_desm/1000, col=viridis::viridis(256))
grau_urb <- rast("grau_urb.tif") %>%
mask(vect(am_legal))
plot(grau_urb, main="Grau de urbanização", col=viridis::viridis(256))
A princípio a amostragem vai ser feita por via de pontos aleatórios. Por enquanto vamos usar 5000 pontos:
set.seed(1)
amostras <- st_sample(am_legal, 5000) %>%
st_sf('ID' = seq(length(.)), 'geometry' = .)
ggplot()+
geom_sf(data=am_legal, col="gray", fill=NA)+
geom_sf(data=amostras, col="red", pch= 3, alpha=0.3)+
theme_bw()
Extração dos dados rasteriais e conversão para tabela:
amostras <- vect(amostras)
dados <- tibble(
focos_10km2=terra::extract(focos_km2, amostras)[,2],
densidade=terra::extract(pt_dens, amostras)[,2],
dist_desm=terra::extract(dist_desm, amostras)[,2],
dist_rod=terra::extract(dist_rod, amostras)[,2],
grau_urb=terra::extract(grau_urb, amostras)[,2]
) %>%
drop_na(densidade)
dados
Traduzindo em forma de gráfico:
ggplot(dados, aes(dist_rod/1000, densidade))+
geom_point(alpha=0.3)+
ylab("Focos por km²")+
xlab("Distância à rodovia (km)")
ggplot(dados, aes(dist_desm/1000, densidade))+
geom_point(alpha=0.3)+
ylab("Focos por km²")+
xlab("Distância ao desmatamento (km)")
ggplot(dados, aes(grau_urb, densidade))+
geom_point(alpha=0.3)+
ylab("Focos por km²")+
xlab("Grau de urbanização")
Para começar, um teste usando RandomForest para fazer uma regressão:
cl <- makePSOCKcluster(5)
registerDoParallel(cl)
modelo_rf <- dados %>%
train(densidade ~ dist_rod + dist_desm + grau_urb, data=.)
## note: only 2 unique complexity parameters in default grid. Truncating the grid to 2 .
stopCluster(cl)
modelo_rf
## Random Forest
##
## 4955 samples
## 3 predictor
##
## No pre-processing
## Resampling: Bootstrapped (25 reps)
## Summary of sample sizes: 4955, 4955, 4955, 4955, 4955, 4955, ...
## Resampling results across tuning parameters:
##
## mtry RMSE Rsquared MAE
## 2 0.1443886 0.5798548 0.09584884
## 3 0.1408890 0.6000214 0.09242855
##
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was mtry = 3.
dados2 <- dados %>%
mutate(preds = predict(modelo_rf, newdata=.), erro=(densidade-preds))
Comparação com a realidade:
dados2 %>%
ggplot(aes(dist_rod/1000, densidade))+
geom_point(aes(color="Observado"),alpha=0.3)+
geom_point(aes(y=preds, color="Previsto"), alpha=0.3)+
xlab("Distância à rodovia")+
ylab("Densidade")
dados2 %>%
ggplot(aes(dist_desm/1000, densidade))+
geom_point(aes(color="Observado"),alpha=0.3)+
geom_point(aes(y=preds, color="Previsto"), alpha=0.3)+
xlab("Distância ao desmatamento")+
ylab("Densidade")
dados2 %>%
ggplot(aes(grau_urb, densidade))+
geom_point(aes(color="Observado"),alpha=0.3)+
geom_point(aes(y=preds, color="Previsto"), alpha=0.3)+
xlab("Grau de urbanização")+
ylab("Densidade")
Distribuição de erros:
dados2 %>%
ggplot(aes(x=erro))+
geom_histogram(bins=50)+
scale_x_continuous(breaks=seq(-1,1,0.1))
Enfim classificar a imagem:
dados_stack <- rast(list(dist_desm, resample(dist_rod, dist_desm), resample(grau_urb, dist_desm)))
pred_rast <- predict(dados_stack, modelo_rf, na.rm=T, cores=2)
plot(pred_rast, col=colorPal)
par(mfrow=c(1,2))
plot(main="Previsto", pred_rast, col=colorPal)
plot(main="Observado", pt_dens, col=colorPal)
Alguns pontos onde é possível melhorar: